package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartInfoAsyncService;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.exception.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author: 李旭
 * @create: 2021-09-07 11:27
 **/
@Service
public class CartInfoServiceImpl implements CartInfoService {


    @Autowired
    private ProductFeignClient productFeignClient;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CartInfoMapper cartInfoMapper;
    @Autowired
    private CartInfoAsyncService cartInfoAsyncService;

    //加入购物车
    @Override
    @Transactional
    public void addCart(Long skuId, Integer skuNum, String userId) {
        //临时定义一个用户ID
        //String userId = "3";
        //1:查询Redis中是否有当前我要加入的商品
        // Hash类型
        // Map user:userId:cart = new HashMap
        //cart:userId:info.put(skuId.toString(),CartInfo)
        String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userId
                + RedisConst.USER_CART_KEY_SUFFIX;
        CartInfo cartInfo = (CartInfo) redisTemplate.opsForHash().
                get(cacheKeyCart, skuId.toString());
        if (null != cartInfo) {
            //2:有 直接追加数量
            cartInfo.setSkuNum(cartInfo.getSkuNum() + skuNum);
            //2.1:选中追加数量的商品
            cartInfo.setIsChecked(1);
            //双写
            //DB中
            //第一种方式： cartInfoMapper.updateById(cartInfo);
            //第二种方式：开启子线程方式  更新的条件 userId+SkuId
            System.out.println("主线程:" + Thread.currentThread().getName());
            cartInfoAsyncService.updateAsync(cartInfo);
        } else {
            //3:没有 添加新商品到购物车中
            cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            //库存信息
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            if(null != skuInfo && null != skuInfo.getId()){
                cartInfo.setSkuNum(skuNum);
                cartInfo.setImgUrl(skuInfo.getSkuDefaultImg());
                cartInfo.setSkuName(skuInfo.getSkuName());
                cartInfo.setIsChecked(1);
                //实时价格
                BigDecimal price = productFeignClient.getPrice(skuId);
                cartInfo.setCartPrice(price);//放入购物中的时候价格
                //双写
                //DB中
                //第一种方式： cartInfoMapper.insert(cartInfo);
                //第二种方式：开启子线程方式
                cartInfoAsyncService.insertAsync(cartInfo);
            }else{
                throw new GmallException(ResultCodeEnum.FAIL);
            }
        }
        //缓存中
        redisTemplate.opsForHash().put(cacheKeyCart,skuId.toString(),cartInfo);
    }

    //查询购物车 根据库存ID 查询一个购物车
    @Override
    public CartInfo findCartInfo(Long skuId) {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String userId = AuthContextHolder.getUserId(request);
        if(StringUtils.isEmpty(userId)){
            userId = AuthContextHolder.getUserTempId(request);
        }
        //String userId = "3";
        String cacheKeyCartByUserId = RedisConst.USER_KEY_PREFIX + userId
                + RedisConst.USER_CART_KEY_SUFFIX;

        //判断当前Key值在缓存是否存在
        if(redisTemplate.opsForHash().hasKey(cacheKeyCartByUserId,skuId.toString())){
            //优先真实用户的购物车
            return (CartInfo) redisTemplate.opsForHash().
                    get(cacheKeyCartByUserId, skuId.toString());
        }else{
            //其次临时用户的购物车
            String cacheKeyCartByUserTempId = RedisConst.USER_KEY_PREFIX
                    + AuthContextHolder.getUserTempId(request)
                    + RedisConst.USER_CART_KEY_SUFFIX;
            return (CartInfo) redisTemplate.opsForHash().
                    get(cacheKeyCartByUserTempId, skuId.toString());
        }


    }

    //加载购物车集合
    @Override
    @Transactional
    public List<CartInfo> cartList(String userId, String userTempId) {
        //1:判断真实用户ID是否存在
        if(!StringUtils.isEmpty(userId)){
            //登录了
            if(!StringUtils.isEmpty(userTempId)){
                //临时用户是存在的  返回值：大合并 后购物车集合
                return mergeCartInfoList(userId,userTempId);
            }else{
                //临时用户不存在  返回值 ： 真实用户的购物车集合
                return getCartInfoList(userId);
            }
        }else{
            //未登录  返回值 ： 临时用户的购物车集合
            return getCartInfoList(userTempId);
        }
    }
    //更新购物车的选中或取消状态
    @Override
    @Transactional
    public void checkCart(Long skuId, Integer isChecked, String userId) {
        //1:更新Db
        CartInfo cartInfo = new CartInfo();
        cartInfo.setIsChecked(isChecked);
        cartInfoMapper.update(cartInfo,
                new QueryWrapper<CartInfo>()
                .eq("user_id",userId)
                .eq("sku_id",skuId));
        //2:更新缓存
        String cacheKeyCart = RedisConst.USER_KEY_PREFIX
                + userId
                + RedisConst.USER_CART_KEY_SUFFIX;
        cartInfo = (CartInfo) redisTemplate.opsForHash().get(cacheKeyCart, skuId.toString());
        cartInfo.setIsChecked(isChecked);
        redisTemplate.opsForHash().put(cacheKeyCart,skuId.toString(),cartInfo);

    }



    // 大合并 后购物车集合   代码超难的位置
    private List<CartInfo> mergeCartInfoList(String userId, String userTempId) {
        //用户ID  用户的ID 对应购物车集合吗？
        //1:根据用户ID获取购物车集合   真实用户ID  临时用户ID
        //1.1: 获取真实用户ID的购物车集合
        List<CartInfo> cartInfoListByUserId = getCartInfoList(userId);
        //1.2:获取临时用户ID的购物车集合
        List<CartInfo> cartInfoListByUserTempId = getCartInfoList(userTempId);
        //2:判断真实用户的购物车集合是否存在
        if(!CollectionUtils.isEmpty(cartInfoListByUserId)){
            //真实用户购物车集合是存在的
            //2.1:判断临时用户的购物车集合是否存在
            if(!CollectionUtils.isEmpty(cartInfoListByUserTempId)){
                ////真实用户购物车集合是存在的 同时 临时用户的购物车集合存在
                //Map <K,V>  K:skuId  V:CartInfo
                Map<Long, CartInfo> cartInfoMapByUserId = cartInfoListByUserId.stream().collect(
                        Collectors.toMap(CartInfo::getSkuId, cartInfoByUserId -> cartInfoByUserId));
                //大合并
                for (CartInfo cartInfoByUserTempId : cartInfoListByUserTempId) {
                    //判断
                    CartInfo cartInfoByUserId = cartInfoMapByUserId.get(
                            cartInfoByUserTempId.getSkuId());
                    if(null != cartInfoByUserId){
                        //2.2: 合并过程中 如果遇到同款商品 追加数量  同款：skuId相同
                        cartInfoByUserId.setSkuNum(
                                cartInfoByUserId.getSkuNum()
                                        + cartInfoByUserTempId.getSkuNum());
                        //设置选中
                        cartInfoByUserId.setIsChecked(1);
                        //DB 持久化操作
                        //2.2.1:  更新DB中数量
                        cartInfoMapper.update(cartInfoByUserId
                        ,new QueryWrapper<CartInfo>()
                                        .eq("user_id",cartInfoByUserId.getUserId())
                                        .eq("sku_id",cartInfoByUserId.getSkuId()));
                        //2.2.2: 删除 临时用户ID+skuId 组合 对应的数据
                        cartInfoMapper.delete(new QueryWrapper<CartInfo>()
                                .eq("user_id",cartInfoByUserTempId.getUserId())
                                .eq("sku_id",cartInfoByUserTempId.getSkuId()));
                        //2.2.3:   redis put 覆盖原来的数据
                        String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userId
                                + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(cacheKeyCart,
                                cartInfoByUserId.getSkuId().toString(),cartInfoByUserId);
                    }else{
                        //2.3: 合并过程中 如果没有同款商品 作为新款商品进行添加
                        cartInfoMapByUserId.put(cartInfoByUserTempId.getSkuId(),cartInfoByUserTempId);
                        //DB 持久 化
                        //2.3.1: 更新临时用户ID为真实用户ID
                        cartInfoByUserTempId.setUserId(userId);
                        cartInfoMapper.update(cartInfoByUserTempId,
                                new QueryWrapper<CartInfo>()
                              .eq("user_id",userTempId)
                              .eq("sku_id",cartInfoByUserTempId.getSkuId()));
                        //2.3.2: 更新缓存中数据
                        String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userId
                                + RedisConst.USER_CART_KEY_SUFFIX;
                        redisTemplate.opsForHash().put(cacheKeyCart,
                                cartInfoByUserTempId.getSkuId().toString(),cartInfoByUserTempId);

                    }
                }
                //删除缓存中临时用户的购物车（Hash）
                String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userTempId
                        + RedisConst.USER_CART_KEY_SUFFIX;
                redisTemplate.delete(cacheKeyCart);
                //合并完成时 返回值 Map
                //return (List<CartInfo>) cartInfoMapByUserId.values();//强转异常
                //在构造器中 将集合转成数组  将数据进行循环遍历将数据移动到新的ArrayList集合中
                return new ArrayList<>(cartInfoMapByUserId.values());//正确
            }else{

                //临时用户购物车集合是不存在的
                return cartInfoListByUserId;
            }
        }else{
            //真实用户购物车集合是不存在的
            //DB 持久化    将临时用户的购物车集合更新成真实用户的
            CartInfo cartInfo = new CartInfo();
            cartInfo.setUserId(userId);
            cartInfoMapper.update(cartInfo,
                    new QueryWrapper<CartInfo>()
                            .eq("user_id",userTempId));
            // update cart_info set user_id = 3 where  user_id = 'sdfafsfsadfasdfafa'
            //Redis缓存持久化
            //Map  user:sdfafsfsadfasdfafa:cart = new hashMap
            //先查询出缓存中所有的临时用户对应集合  将集合批量用户的用户Id
            // 再保存到新的Redis缓存中

            String cacheKeyCartByUserTempId = RedisConst.USER_KEY_PREFIX + userTempId
                    + RedisConst.USER_CART_KEY_SUFFIX;
            List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheKeyCartByUserTempId);
            Map<String, CartInfo> map = cartInfoList.stream().peek(cartInfo1 -> {
                //设置真实用户的ID
                cartInfo1.setUserId(userId);
            }).collect(Collectors.toMap(cartInfo1 -> {
                return cartInfo1.getSkuId().toString();
            }, cartInfo1 -> cartInfo1));
            String cacheKeyCartByUserId = RedisConst.USER_KEY_PREFIX + userId
                    + RedisConst.USER_CART_KEY_SUFFIX;
            redisTemplate.opsForHash().putAll(cacheKeyCartByUserId,map);
            redisTemplate.delete(cacheKeyCartByUserTempId);
            return cartInfoListByUserTempId;
        }
    }

    //查询此用户的购物车集合   大量重复使用的方法
    private List<CartInfo> getCartInfoList(String userId) {
        String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userId
                + RedisConst.USER_CART_KEY_SUFFIX;
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().values(cacheKeyCart);
        //同学们提出的问题  解决一下
        if(!CollectionUtils.isEmpty(cartInfoList)){
            //更新实时价格
            cartInfoList.forEach(cartInfo -> {
                BigDecimal price = productFeignClient.getPrice(cartInfo.getSkuId());
                cartInfo.setSkuPrice(price);
            });
        }
        return cartInfoList;
    }

    //查询购物车中已经选中了商品
    @Override
    public List<CartInfo> findCartInfoListByIsChecked(String userId) {
        String cacheKeyCart = RedisConst.USER_KEY_PREFIX + userId
                + RedisConst.USER_CART_KEY_SUFFIX;
        //查询所有购物车集合  优化：同学们完成
        List<CartInfo> cartInfoList = redisTemplate.opsForHash().
                values(cacheKeyCart);
        //只要选中的购物车集合
        return cartInfoList.stream().filter(cartInfo -> {
            if(cartInfo.getIsChecked() == 1){
                return true;//过滤条件 ： 留下
            }
            return false;// false:过滤掉的
        }).peek(cartInfo -> {
            //实时价格
            BigDecimal price = productFeignClient.getPrice(cartInfo.getSkuId());
            cartInfo.setSkuPrice(price);
        }).collect(Collectors.toList());

    }
}
